package e.odbo.data.util.en;

import com.openbravo.data.loader.serialize.DataRead;
import com.openbravo.data.loader.serialize.DataWrite;
import com.openbravo.data.loader.serialize.Datas;
import e.odbo.data.dao.table.Field;
import e.odbo.data.dao.table.TableDefinition;
import e.odbo.data.generator.Generator;
import e.odbo.data.model.*;
import e.odbo.data.util.ArrayUtils;
import e.odbo.data.util.ClassUtils;
import e.odbo.data.util.StringUtils;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;


public class TableUtils {

    public static void printColumnAndType(Table table){
        System.out.print(buildColumnAndType(table));
    }

    public static String buildColumnAndType(Table table){
        StringBuilder ret=new StringBuilder("/**start table:\t"+table.getName()+"\n");
        ret.append("-----v--------------------v-----------------\n");
        String[] columnNames=table.getColumnNames();
        ret.append("index|     columnName     | type\n");
        ret.append("  -  |           -        | -\n");
        Datas[] datas=table.getColumnDatas();
        for(int i=0;i<datas.length;i++)
            ret.append(String.format("%3d  |%15s     |\t%s\n",i,columnNames[i],datas[i].getClassValue().getName()));
        ret.append("-----^--------------------^-----------------**/\n");
        return ret.toString();
    }


    //TableStruct
    public static void printTableStruct(Table table){
        StringBuilder tableDefinitionBuilder=new StringBuilder();
        buildTableStruct(table,tableDefinitionBuilder);
        System.out.print(tableDefinitionBuilder.toString());
    }

    public static void buildTableStruct(Table table,StringBuilder builder){
        builder.append(String.format("## %s",table.getName()));
        if(null!=table.getComment())
            builder.append("("+table.getComment()+")");
        builder.append("\n\n");
        Column[] columns=table.getColumns();

        StringBuilder memoBuilder=new StringBuilder("MEMO:    ~:Forerign Key; A:AUTO INC; N:NOT NULL\n\n");

        buildTableStruct(columns,builder,memoBuilder);

        if(!table.getIndexs().isEmpty()) {
            memoBuilder.append("    Index:");
            for (Map.Entry<String, String[]> index : table.getIndexs().entrySet()) {
                memoBuilder.append(String.format("\t%s(%s)", index.getKey(), StringUtils.join(index.getValue(), ',')));
            }
            memoBuilder.append("\n");
        }

        if(!table.getForeignKeys().isEmpty()) {
            memoBuilder.append("    ForeignKeys:");
            for (TableForeignKey foreignKey : table.getForeignKeys()) {
                buildTableStruct(foreignKey,memoBuilder);
            }
            memoBuilder.append("\n");
        }
        memoBuilder.append("-----\n\n");
        builder.append(memoBuilder);
        builder.append("[-> Home](#自动生成库说明) \n");
    }

    public static void buildTableStruct(Column[] columns,StringBuilder builder,StringBuilder memoBuilder) {
        builder.append("|index|   columnName   |      type      |     sqltype     |  memo\n");
        builder.append("|  -  |         -      | -              |  -              |-\n");
        for(int i=0;i<columns.length;i++) {
            buildTableStruct(i,columns[i],builder,memoBuilder);
        }
        builder.append("\n");
    }


    private static void buildTableStruct(int order,Column column,StringBuilder builder,StringBuilder memoBuilder) {
        StringBuilder columnMemo=new StringBuilder();
        String typeName=ClassUtils.getPackageBaseClassName(column.getColumnType().getDataType().getClassValue());
        String sqlType=column.getColumnType().buildTypeString();
        builder.append(String.format("|%3d  |%16s|%16s|%17s|%s\n", order, getColumnNameWithFlag(column),typeName,sqlType,column.getComment()==null?"-":column.getComment()));
        Collection<ColumnType> dialactTypes=column.getDialectColumnTypes();
        if(null!=dialactTypes) {
            for (ColumnType columnType : dialactTypes) {
                columnMemo.append(String.format("\t%10s:%s", columnType.getDialectName(), columnType.buildTypeString()));
            }
        }
        if(null!=column.getColumnType().getDefaultValue())
            columnMemo.append("\tdefalue->"+column.getColumnType().getDefaultValue());
        if(null!=column.getColumnType().getDefaultGenerator())
            columnMemo.append("\tGenerator Class:"+ClassUtils.getPackageBaseClassName(column.getColumnType().getDefaultGenerator().getClass()));
        if(!columnMemo.toString().isEmpty()) {
            memoBuilder.append("    (" + order + ")" + column.getName() + ":");
            memoBuilder.append(columnMemo);
            memoBuilder.append("\n");
        }
    }


    public static void buildTableStruct(TableForeignKey foreignKey,StringBuilder memoBuilder) {
        memoBuilder.append(String.format("\t%s(%s)->[%s](#%s) (%s)",foreignKey.getTable(), StringUtils.join(foreignKey.getColumns(), ','),foreignKey.getRefTable(), foreignKey.getRefTable(), StringUtils.join(foreignKey.getRefColumns(), ',')));
        if(foreignKey.isCascadeFlag_del())
            memoBuilder.append("\t on delete "+foreignKey.getCascadeFlag_del().getName()+"\t");
        if(foreignKey.isCascadeFlag_update())
            memoBuilder.append("\t on update "+foreignKey.getCascadeFlag_update().getName()+"\t");
    }


    private static String getColumnNameWithFlag(Column column){
        String name=column.getName();
        if(column.isPK())
            name+="(PK)";
        else if(column.isNoNull())
            name+="(N)";
        if(column.isRefColumn())
            name+="(~)";
        if(column.isAutoGreater())
            name+="(A)";

        return name;
    }

    //DataStruct
    public static void printTableDataStruct(Table table){
        StringBuilder ret=new StringBuilder("insert tableData table:\t"+table.getName()+"\n\n");
        ret.append("TableData "+table.getName().toLowerCase()+"TableData=TableData.C(\""+table.getName()+"\"");
        TableData tableData=TableData.C(table,true);
        for(String column:tableData.getColumn())
           ret.append(",\""+column+"\"");
        ret.append(" )\n\t\t\t\t\t\t.types(");//+table.getName().toLowerCase()+".getColumnDatas());"+"\n\t\t\t\t//types: ");
        Column[] columns=table.getColumns(tableData.getColumn());
        String typeName=null;
        for(int i=0;i<columns.length;i++) {
            if(i>0)
                ret.append(" , ");
            typeName=ClassUtils.getPackageBaseClassName(columns[i].getColumnType().getDataType().getClass());
            ret.append(typeName);
        }
        ret.append(")");
        Column column=null;
        for(TableData.GenerateColumn generateColumn:tableData.getGenerateColumns()) {
            column=table.getColumn(generateColumn.getColumn());
            typeName=ClassUtils.getPackageBaseClassName(column.getColumnType().getDataType().getClass());
            ret.append(".autoValueColumn(\""+column.getName()+"\","+typeName+",");
            if(null!=column.getColumnType().getDefaultValue())
                ret.append("Generator.DEFAULT("+table.getName().toLowerCase()+".getColumn(\""+column.getName()+"\").getColumnType().getDefaultValue()");
            else
                ret.append(table.getName().toLowerCase()+".getColumn(\""+column.getName()+"\").getColumnType().getDefaultGenerator()");
            ret.append(")");
        }
        ret.append(";\n\t\t\t\t//types: ");
        for(int i=0;i<columns.length;i++) {
            if(i>0)
                ret.append(" , ");
            typeName=columns[i].getColumnType().getDataType().getClassValue().getName();
            typeName=typeName.substring(typeName.lastIndexOf(".")+1);
            ret.append(typeName);
            if(columns[i].isPK())
                ret.append("(PK)");
            else if(columns[i].isNoNull())
                ret.append("(notNull)");
            if(columns[i].isRefColumn())
                ret.append("(->:"+columns[i].getRefTable()+"-"+columns[i].getRefColumn()+")");
        }
        ret.append(";\n");
        System.out.print(ret.toString());
    }


    //dao
    private static DateFormat tsf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    public static void printTableDAO(Table table){
        StringBuilder tableDefinitionBuilder=new StringBuilder();
        buildTableDAOClass(table,tableDefinitionBuilder);
        System.out.print(tableDefinitionBuilder.toString());
    }

    public static void buildTableDAOClass(Table table,StringBuilder tableDAOBuilder){
        List<String> importClassList=new ArrayList<>();
        buildTableDefinitionClassImport(table,importClassList);
        tableDAOBuilder.append("\n\n");
        tableDAOBuilder.append("public class "+ClassUtils.getClassName(table.getName())+"DAO "+getExtendsDAOClass(table,importClassList));

        tableDAOBuilder.append("{\n" +
                "\n" +
                "    public "+ClassUtils.getClassName(table.getName())+"DAO(I_Session s) {\n" +
                "        super(s);\n" +
                "    }");
        tableDAOBuilder.append("\n\n");
        buildTableDefinition(table,tableDAOBuilder,importClassList);
        tableDAOBuilder.append("\n\n");
        buildTableDAOGeterAndSeter(table,tableDAOBuilder,importClassList);
        tableDAOBuilder.append("\n\n");
        StringBuilder importBuilder=new StringBuilder();
        for(String classImport:importClassList)
            importBuilder.append("import "+classImport+";\n");
        tableDAOBuilder.insert(0,importBuilder.toString());

        tableDAOBuilder.append("}");
    }

    private static void buildTableDAOGeterAndSeter(Table table,StringBuilder tableDAOBuilder,List<String> importClassList){
        String beanClassName=ClassUtils.getClassName(table.getName());
        StringBuilder geterBuilder=new StringBuilder("@Override\n" +
                "    public void writeInsertValues(DataWrite dp,"+beanClassName+" obj) throws BasicException {\n");

        StringBuilder seterBuilder=new StringBuilder("public "+beanClassName+" readValues(DataRead dr,"+ClassUtils.getClassName(table.getName())+" obj) throws BasicException {\n");

        seterBuilder.append("\t\tif(obj==null)\n" +
                "        \t   obj = new "+beanClassName+"();\n");

        Column[] columns=table.getColumns();
        for(int i=0;i<columns.length;i++){
            seterBuilder.append("\t\tobj."+ClassUtils.getFieldMethodSeter(columns[i].getName())+"(dr.get"+ClassUtils.getPackageBaseClassName(columns[i].getColumnType().getDataType().getClassValue())+"("+i+"));\n");
            geterBuilder.append("\t\tdr.set"+ClassUtils.getPackageBaseClassName(columns[i].getColumnType().getDataType().getClassValue())+"("+i+",obj."+ClassUtils.getFieldMethodGeter(columns[i].getName())+"());\n");
            if(i>0&&i%5==0){
                seterBuilder.append("\n");
                geterBuilder.append("\n");
            }
        }
        seterBuilder.append("\t}\n");
        geterBuilder.append("\t}\n");
        tableDAOBuilder.append(geterBuilder);
        tableDAOBuilder.append("\n\n");
        tableDAOBuilder.append(seterBuilder);

        addImportClass(DataWrite.class,importClassList);
        addImportClass(DataRead.class,importClassList);
    }

    private static String getExtendsDAOClass(Table table,List<String> importClassList){
        return "";
    }


    //TableDefinition
    public static void printTableDefinition(Table table){
        StringBuilder rt=new StringBuilder();
        buildTableDefinition(table,rt,new ArrayList<String>());
        System.out.print(rt.toString());
    }

    public static void buildTableDefinition(Table table,StringBuilder createTableDefinitionBuilder,List<String> importClassList){
        createTableDefinitionBuilder.append("// tableDefinition table:\t"+table.getName()+" \tbuild time :"+ tsf.format(new Date()) +"\n\n");
        createTableDefinitionBuilder.append("\t\t@Override\n");
        createTableDefinitionBuilder.append("public TableDefinition getTable() {\n");
        createTableDefinitionBuilder.append("    TableDefinition "+table.getName().toLowerCase()+"TableDefinition=new TableDefinition('"+table.getName().toLowerCase()+"'");
        createTableDefinitionBuilder.append(" ,new Field[]{\n");

        Column[] columns=table.getColumns();
        List<Integer> pkColumns=new ArrayList<>();
        String typeName=null;
        Class toCheckClass=null;
        String tableBeanClassName=ClassUtils.getClassName(table.getName());

        for(int i=0;i<columns.length;i++) {

            createTableDefinitionBuilder.append("                    new Field("+tableBeanClassName+"."+columns[i].getName().toUpperCase());

            toCheckClass=columns[i].getColumnType().getDataType().getClass();
            typeName=ClassUtils.getPackageBaseClassName(toCheckClass);
            createTableDefinitionBuilder.append(","+typeName);


            toCheckClass=columns[i].getColumnType().getFormat().getClass();
            typeName=ClassUtils.getPackageBaseClassName(toCheckClass);
            createTableDefinitionBuilder.append(","+typeName);

            if(columns[i].isRefColumn())
                createTableDefinitionBuilder.append(","+ClassUtils.getClassName(columns[i].getRefTable()));

            createTableDefinitionBuilder.append(")");

            if(i<columns.length-1)
                createTableDefinitionBuilder.append(",");
            createTableDefinitionBuilder.append("//");
            if(columns[i].isPK()) {
                createTableDefinitionBuilder.append("(PK)");
                pkColumns.add(i);
            }
            else if(columns[i].isNoNull())
                createTableDefinitionBuilder.append("(notNull)");
            if(columns[i].isRefColumn())
                createTableDefinitionBuilder.append("ref to:"+columns[i].getRefTable()+"/"+columns[i].getRefColumn());
            createTableDefinitionBuilder.append("\n");
        }

        createTableDefinitionBuilder.append("       }, new int[] {"+StringUtils.join(pkColumns.iterator(),",")+"}\n");

        createTableDefinitionBuilder.append("    );\n");
        createTableDefinitionBuilder.append("    return "+table.getName().toLowerCase()+"TableDefinition;\n");
        createTableDefinitionBuilder.append("}");
    }


    public static void buildTableDefinitionClassImport(Table table,List<String> importClassList){
        Class toCheckClass=null;

        addImportClass(TableDefinition.class,importClassList);
        addImportClass(Field.class,importClassList);

        Column[] columns=table.getColumns();
        for(int i=0;i<columns.length;i++) {
            toCheckClass=columns[i].getColumnType().getDataType().getClass();
            addImportClass(toCheckClass,importClassList);

            toCheckClass=columns[i].getColumnType().getFormat().getClass();
            addImportClass(toCheckClass,importClassList);
        }
    }

    //公共
    public static void buildStaticTableFields(Table table,StringBuilder staticFieldName) {
        Column[] columns = table.getColumns();
        for (int i = 0; i < columns.length; i++) {
            staticFieldName.append("public final static String " + columns[i].getName().toUpperCase() + "=\"" + columns[i].getName().toUpperCase() + "\";");
            if (null != columns[i].getComment())
                staticFieldName.append("//" + columns[i].getComment());
            staticFieldName.append("\n");
        }
    }

    private static void addImportClass(Class importClass,List<String> importClassList){
        String importClassName=ClassUtils.getImportClassName(importClass);
        if(!importClassList.contains(importClassName))
            importClassList.add(importClassName);

    }

    //__________Bean_______________
    public static void printTableBean(Table table){
        StringBuilder stringBuilder=new StringBuilder();
        buildTableBean(table,stringBuilder);
        System.out.print(stringBuilder.toString());
    }

    private static String getBeanExtendsClass(Table table,List<String> importClassList){
        return "";
    }

    public static void buildTableBean(Table table,StringBuilder beanBuilder){
        List<String> importClassList=new ArrayList<>();

        beanBuilder.append("\n\n");
        beanBuilder.append("public class "+ClassUtils.getClassName(table.getName())+" "+getBeanExtendsClass(table,importClassList));

        beanBuilder.append("{\n\n" );

        beanBuilder.append("//private static final long serialVersionUID ="+ Generator.LONG.generator() +"L;\n\n");

        buildStaticTableFields(table,beanBuilder);

        beanBuilder.append("\n");

        buildBeanProperty(table,beanBuilder,importClassList);

        beanBuilder.append("\n" +
                "    public "+ClassUtils.getClassName(table.getName())+"() {\n" +
                "        super();\n" +
                "    }");
        beanBuilder.append("\n\n");

        buildBeanPropertyGeterAndSeter(table,beanBuilder,importClassList);


        StringBuilder importBuilder=new StringBuilder();
        for(String classImport:importClassList)
            importBuilder.append("import "+classImport+";\n");
        beanBuilder.insert(0,importBuilder.toString());

        beanBuilder.append("}");
    }

    private static void buildBeanProperty(Table table,StringBuilder beanBuilder,List<String> importClassList){
        Column[] columns=table.getColumns();
        for(int i=0;i<columns.length;i++){
            beanBuilder.append(" protected  "+ClassUtils.getPackageBaseClassName(columns[i].getColumnType().getDataType().getClassValue())+" "+ClassUtils.getFieldName(columns[i].getName())+";\n");
        }
    }

    private static void buildBeanPropertyGeterAndSeter(Table table,StringBuilder beanBuilder,List<String> importClassList){
        Column[] columns=table.getColumns();
        StringBuilder toString=new StringBuilder("public String toString() {\n");
        toString.append("     return ");
        String type=null;
        String fieldName=null;
        for(int i=0;i<columns.length;i++){
            type=ClassUtils.getPackageBaseClassName(columns[i].getColumnType().getDataType().getClassValue());
            fieldName=ClassUtils.getFieldName(columns[i].getName());
            beanBuilder.append("public "+type+" "+ClassUtils.getFieldMethodGeter(columns[i].getName())+"() {\n" +
                    "        return "+fieldName+";\n" +
                    "  }\n\n");

            beanBuilder.append(" public void "+ClassUtils.getFieldMethodSeter(columns[i].getName())+"("+type+" "+fieldName+") {\n" +
                    "       this."+fieldName+" = "+fieldName+";\n" +
                    "  }\n\n");
            if(i>0) {
                toString.append("\t\t\t+");
                toString.append("\"," + fieldName + "\"=" + fieldName );
                if(i==columns.length-1)
                   toString.append(";");
                else
                    toString.append("\n");
            }else
                toString.append("\"" + fieldName + "\"=" + fieldName + "\n");
        }
        toString.append("\n\t}\n");
        beanBuilder.append("\n\n");
        beanBuilder.append(toString);
    }

    //resource
    public static void printBuildI18n(Table table,String prefix,String temp){
        StringBuilder beanBuilder=new StringBuilder();
        buildI18nUseColumnComment(table,beanBuilder,prefix,temp);
        System.out.print(beanBuilder.toString());
    }
    public static void printBuildI18nWithJava(Table table,String prefix){
        printBuildI18n(table,prefix,"%s=%s");
    }

    public static void printBuildI18nWithAndroid(Table table,String prefix){
        printBuildI18n(table,prefix,"<string name=\"%s\">%s</string>");
    }

    public static void printBuildI18nWithJavaScript(Table table,String prefix){
        printBuildI18n(table,prefix,"%s='%s'");
    }

    public static void printBuildI18nWithJson(Table table){
        StringBuilder beanBuilder=new StringBuilder();
        buildI18nJsonUseColumnComment(table,beanBuilder,"%s='%s'");
        System.out.print(beanBuilder.toString());
    }

    public static void buildI18nUseColumnComment(Table table,StringBuilder beanBuilder,String prefix,String tempString){
        Column[] columns=table.getColumns();

        if(null!=table.getComment()) {
            beanBuilder.append(String.format(tempString, ClassUtils.getI18NString(prefix, table.getName()), table.getComment())+"\n");
        }
        for(int i=0;i<columns.length;i++){
            if(null==columns[i].getComment()||columns[i].getComment().isEmpty())
                continue;
            beanBuilder.append(String.format(tempString,ClassUtils.getI18NString(prefix, table.getName(), columns[i].getName()),columns[i].getComment())+"\n");
        }
        beanBuilder.append("\n");
    }

    public static void buildI18nJsonUseColumnComment(Table table, StringBuilder beanBuilder, String tempString){
        List<Column> columns=ArrayUtils.filter(table.getColumns(), new Comparable<Column>() {
            @Override
            public int compareTo(Column o) {
                return null!=o.getComment()?0:1;
            }
        });

        beanBuilder.append("\t"+table.getName()+":{\n");
        if(null!=table.getComment()) {
            beanBuilder.append("\t\t"+String.format(tempString, table.getName(), table.getComment())+",\n");
        }
        Column column=null;
        for(int i=0;i<columns.size();i++){
            column=columns.get(i);
            if(null==column.getComment()||column.getComment().isEmpty())
                continue;
            beanBuilder.append("\t\t"+String.format(tempString, column.getName(),column.getComment()));
            if(i<columns.size()-1)
                beanBuilder.append(",");
            beanBuilder.append("\n");
        }
        beanBuilder.append("\t}");
    }


    protected static String buildNameKey(String table,String ...columns){
        StringBuilder base=new StringBuilder(table);
        for(String column:columns)
            base.append("_"+column);
        return base.toString();
    }
}
